<!DOCTYPE html>
<html>
    <head>
        <title>Add new disk to RAID</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
        <link rel="stylesheet" href="../../../script/semantic/semantic.min.css">
        <script type="text/javascript" src="../../../script/jquery.min.js"></script>
        <script type="text/javascript" src="../../../script/semantic/semantic.min.js"></script>
        <script type="text/javascript" src="../../../script/ao_module.js"></script>
        <style>
            .installedDisk .checkboxWrapper{
                position: absolute;
                right: 1em;
                top: 0.8em;
            }

            .installedDisk:not(.disabled){
                cursor: pointer;
            }
            .installedDisk:not(.disabled):hover{
                background-color: rgb(243, 243, 243);
            }
            .installedDisk.active:not(.disabled):hover{
                background-color: rgb(207, 239, 255);
            }


            .backicon {
                position: fixed;
                bottom: 0;
                left: 0;
                width: 30%;
                height: calc(50vh - 10px);
                background-image: url('./img/drive-add.svg');
                background-size: contain; 
                background-repeat: no-repeat; 
                margin-left: -3%;
                margin-bottom: -5%;
                opacity: 0.1; 
            }

            .installedDisk.active{
                background-color: rgb(214, 241, 255);
            }

            .advanceinfo{
                background-color: rgb(248, 248, 248) !important;
                border-radius: 0.4em !important;
                padding: 0.6em;
            }

            /* RAID Type space visualizer */
            .raidTypeSpaceVisualizer{
                padding: 0.4em;
                width: 100%;
                display: flex;
            }
            
            .raidTypeSpaceVisualizer .title{
                flex: 0 0 auto;
                padding-left: 1em;
                padding-right: 1em;
                margin-top: 0.6em;
            }

            .raidTypeSpaceVisualizer .bars {
                flex: 1; /* Occupy the remaining space */
                display: flex; /* Nested flex container */
                min-height: 2.6em;
                border-radius: 0.4em;
                overflow: hidden;
                background-color: #c1c1c1;
            }

            .raidTypeSpaceVisualizer .bar{
                text-align: center;
                float: left;
                color: white;
                padding-top: 0.6em;

            }

            .raidTypeSpaceVisualizer .bar.protected{
                background-color: #0067e6;
                width: 50%;
            }
            .raidTypeSpaceVisualizer .bar.usable{
                background-color: #26d4ac;
                width: 50%;
            }
            .raidTypeSpaceVisualizer .bar.wasted{
                background-color: #b5b5b5;
                width: 0%;
            }
           
        </style>
    </head>
    <body>
        <br>
        <div class="backicon"></div>
        <div class="ui container">
            <form class="ui form">
                <h4 class="ui dividing header">1. RAID Volume Settings</h4>
                <div class="field">
                <label>Volume Name</label>
                <div class="field">
                    <input type="text" id="volumeName" placeholder="Volume Name" required>
                    <small>Only alphabet, digits, _ (underscore) and - (hyphen) are allowed</small>
                <h4 class="ui dividing header">2. RAID Disks Settings</h4>
                <div class="ui container">
                    <div style="float:right;">
                        <button class="ui circular icon basic button" onclick="reloadList();"><i class="ui green refresh icon"></i></button>
                    </div>
                    <p>Tips: For any extra disks selected, it will be used as spare disks.</p>
                    <br>
                    <div id="noUsableDisk" class="ui yellow message" style="display:none;">
                        <i class="yellow exclamation triangle icon"></i> There are no usable / spare disks on your system
                    </div>
                    <div id="usablediskSelectionList" style="margin-bottom: 1em;">
        
                    </div>
                    <div id="disableddiskSelectionList">
        
                    </div>
                </div>
                <h4 class="ui dividing header">3. Confirm RAID Level</h4>
                <div class="ui container">
                    <div class="field">
                        <div class="ui selection fluid dropdown">
                            <input type="hidden" id="raidtype" value="raid1" onchange="updateUsableSpaceEstimation();">
                            <i class="dropdown icon"></i>
                            <div class="default text"></div>
                            <div class="menu">
                            <div class="item" data-value="raid1">RAID 1 (Mirror, Recommend)</div>
                            <div class="item" data-value="raid5">RAID 5 (Lose 1 Disk Max.)</div>
                            <!-- <div class="item" data-value="raid6">RAID 6 (Lose 2 Disk Max.)</div> -->
                            <!-- <div class="item" data-value="raid6">RAID 10</div> -->
                            <div class="item" data-value="raid0">RAID 0 (Striped, Not Recommend)</div>
                            </div>
                        </div>
                    </div>
                    <div id="capacityVisualizer" class="ui grey message" style="display: none;">
                        <div class="raidTypeSpaceVisualizer">
                            <div class="title" id="totalDiskSumSize"></div>
                            <div class="bars">
                                <div class="bar usable" id="estimatedUsableSpace"></div>
                                <div class="bar protected" id="estimatedProtectionSpace"></div>
                                <div class="bar wasted" id="estimatedWastedSpace"></div>
                            </div>
                        
                        </div>
                        <div style="float: right;">
                            <div class="ui horizontal label" style="background-color: #26d4ac; color: white;">Available Space</div>
                            <div class="ui horizontal label" style="background-color: #0067e6; color: white;">Redundancy</div>
                            <div class="ui horizontal label" style="background-color: #b5b5b5; color: white;">Unused</div>
                        </div>
                        <br>
                    </div>
                    <p>Match data redundancy requirement disk count: <span id="matchRedundancy">n/a</span></p>
                </div>
                <div id="notEnoughDiskErrorMessage" class="ui red message" style="display:none;">
                    <i class="ui red circle times icon"></i> Not enough disk selected for <span id="errorRaidType"></span>
                </div>
                <br>
                <div class="ui basic right floated button" onclick="createRAID(event);">
                    <i class="ui icons">
                        <i class="grey server icon"></i>
                        <i class="small green add icon" style="margin-top: 8px; margin-left: 8px;"></i>
                    </i>
                    Create RAID
                </div>
            </form>
        </div>
        <br>
      
        <!-- Disk Format Confirmation -->
        <div id="confirmDiskChoice" class="ui modal">
            <i class="close icon"></i>
            <div class="header">
              Confirm Selected Disks
            </div>
            <div class="image content">
              <div class="ui small image">
                <img src="./img/drive-add.svg">
              </div>
              <div class="description">
                <h3 id="oprconfirm"></h3>
                <p><b>Selecting these disk will erase all data current on the disk.</b> Confirm?</p>
                <p id="erasePendingDisk"></p>
                <div class="advanceinfo">
                    <small >
                        <b>Notes on "Continue Without Format"</b><br>
                        If you decided to continue building the RAID volume without formating the disks (usually use for rebuilding an array migrate from another server), please make sure the mdadm version on both server are identical.
                    </small>
                </div>
              
              </div>
            </div>
            <div class="actions">
              <div class="ui positive left labeled icon button" onclick="confirmAddDisk();">
                <i class="check icon"></i>
                Confirm
              </div>
              <div class="ui yellow button" onclick="confirmAddDiskWithoutFormat();">
                <i class="ui exclamation triangle icon"></i> Continue without Format
              </div>
              <div class="ui basic deny button">
                Cancel
              </div>
            </div>
          </div>

        <br><br>
        <script>
            let diskInfo = {};
            let erasePendingDisks = []; //List of disk prepare to be formatted and create an RAID array
            $(".dropdown").dropdown();

            function bytesToSize(bytes) {
                var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
                if (bytes == 0) return 'n/a';
                var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
                if (i == 0) return bytes + ' ' + sizes[i];
                return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
            };

            function uuidv4() {
                return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
                    (+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16)
                );
            }

            /* Resolve disk label / model number to selector DOM element */
            function resolveDiskLabelToDOM(diskPath, domSelector){
                $.get("../../../system/disk/devices/model?devName=" + diskPath, function(data){
                    let diskLabelName = ""
                    if (data.error == undefined){
                        //[0] is disk labeled name
                        //[1] is disk labeled size
                        diskLabelName = data[0];
                    }
                    $(domSelector).html(diskLabelName);
                });
            }

            /* List all the usable disks */
           function initDiskList(){
                $("#disableddiskSelectionList").html(`
                    <div class="ui basic segment advanceinfo">
                        <i class='ui red remove icon'></i> Disk service error or unsupported host operating system
                    </div>
                    <br>
                `);
                $("#usablediskSelectionList").html("");
                $.get("../../../system/disk/devices/list", function(data){
                    if (data.error != undefined){
                        $("#disableddiskSelectionList").html(`
                            <div class="ui basic segment advanceinfo">
                                <i class='ui red remove icon'></i> ${data.error}
                            </div>
                            <br>
                        `);
                    }else{
                        $("#disableddiskSelectionList").html(``);
                        let usableDiskCount = 0;
                        diskInfo = data;
                        data.forEach(function(driveInfo){
                            //Generate the children table
                            let childrenTable = "";
                            let notUsable = false;
                            if (driveInfo.children != undefined){
                                for (var i = 0; i < driveInfo.children.length; i++){
                                    let thisChild = driveInfo.children[i];
                                    childrenTable = childrenTable + `<tr>
                                        <td>${thisChild.name}</td>
                                        <td>${bytesToSize(thisChild.size)}</td>
                                        <td>${thisChild.type}</td>
                                        <td>${thisChild.mountpoint}</td>
                                    </tr>`;

                                    //Check if this disk is already in other raid object
                                    if (thisChild.name.substr(0, 2) == "md" || thisChild.type.substr(0, 4) == "raid"){
                                        //This is already in some raid array
                                        notUsable = true;
                                    }

                                    if (thisChild.mountpoint == "/"){
                                        //This is the root drive! Don't allow self destruct
                                        notUsable = true;
                                    }
                                }
                            }
                            
                            var domUID = uuidv4();
                            let diskDOM = (`<div onclick="handleSelect(this);" dname="${driveInfo.name}" dsize="${driveInfo.size}" class="ui segment installedDisk ${domUID} ${notUsable?"disabled":""}">
                                <h4 class="ui header">
                                    <img src="./img/drive.svg">
                                    <div class="content">
                                        ${bytesToSize(driveInfo.size)} - <span class="diskname ${domUID}">Unknown Disk</span>
                                        <div class="sub header">/dev/${driveInfo.name}</div>
                                    </div>
                                </h4>
                                <div class="children">
                                    <table class="ui very basic unstackable table">
                                        <thead>
                                        <tr>
                                            <th>Name</th>
                                            <th>Size</th>
                                            <th>Partition Type</th>
                                            <th>Mount Point</th>
                                        </tr></thead>
                                        <tbody>${childrenTable}</tbody>
                                    </table>
                                </div>
                            </div>`);
                            
                           

                            if (!notUsable){
                                $("#usablediskSelectionList").append(diskDOM);
                            }else{
                                $("#disableddiskSelectionList").append(diskDOM);
                            }

                            if (driveInfo.children == undefined || driveInfo.children.length == 0){
                                $(".installedDisk." + domUID).find(".children").hide();
                            }
                            //console.log(driveInfo);

                            if (notUsable){
                                $(".installedDisk." + domUID).find(".checkboxWrapper").hide();
                            }

                            resolveDiskLabelToDOM(`/dev/${driveInfo.name}`, ".diskname." + domUID);

                            if (!notUsable){
                                usableDiskCount++;
                            }
                        });

                        if (usableDiskCount == 0){
                            //No usable disk. Is all disk in use?
                            $("#noUsableDisk").show();
                            $("#confirmButton").addClass("disabled");
                        }else{
                            $("#noUsableDisk").hide();
                            $("#confirmButton").removeClass("disabled");
                        }
                    }
                })
            }
            initDiskList();

            //When a disk is selected
            function handleSelect(diskObj){
                if ($(diskObj).hasClass("disabled")){
                    return;
                }
                if ($(diskObj).hasClass("active")){
                    $(diskObj).removeClass('active');
                }else{
                    $(diskObj).addClass('active');
                }
                updateUsableSpaceEstimation();
            }

            /* Space Estimation Function */
            function estimateUsableSpace(diskSizes, raidType) {
                // Calculate total disk space
                const totalSpace = diskSizes.reduce((acc, size) => acc + size, 0);
                
                // Define usable space estimation based on RAID types
                const usableSpaceEstimation = {
                    "raid0": totalSpace,
                    "raid1": Math.min(...diskSizes),
                    "raid5": totalSpace - Math.min(...diskSizes),
                    "raid6": totalSpace - 2 * Math.min(...diskSizes),
                    "raid10": totalSpace / 2
                };

                // Check if RAID type is valid
                if (usableSpaceEstimation.hasOwnProperty(raidType)) {
                    return usableSpaceEstimation[raidType];
                } else {
                    return "Invalid RAID type";
                }
            }

            function estimateProtectionDataSpace(diskSizes, raidType) {
                // Define the disk redundancy factor for each RAID type
                const redundancyFactors = {
                    "raid0": 0,
                    "raid1": 1,
                    "raid5": 1,
                    "raid6": 2,
                    "raid10": 1
                };

                // Check if RAID type is valid
                if (!redundancyFactors.hasOwnProperty(raidType)) {
                    return "Invalid RAID type";
                }

                const redundancyFactor = redundancyFactors[raidType];
                const totalDiskSpace = diskSizes.reduce((acc, size) => acc + size, 0);

                // Calculate the redundancy/protection reserved space
                const protectionSpace = redundancyFactor * Math.min(...diskSizes);

                return protectionSpace;
            }

            function updateUsableSpaceEstimation(){
                //Get the list of disk selected and their size
                let diskSizes = [];
                $(".installedDisk.active").each(function(){
                    let diskSize = $(this).attr("dsize");
                    diskSize = parseInt(diskSize);
                    diskSizes.push(diskSize);
                });

                if (diskSizes.length == 0){
                    //No disk selected
                    $("#estimatedUsableSpace").text("No selected disk");
                    return;
                }

                let totalDiskSize = 0;
                for(var i = 0; i < diskSizes.length; i++){
                    totalDiskSize += diskSizes[i];
                }

                //Generate estimated array size
                let selectedRAIDType = $("#raidtype").val();
                let usableSpace = estimateUsableSpace(diskSizes, selectedRAIDType);
                let redundancySpace = estimateProtectionDataSpace(diskSizes, selectedRAIDType);
                if (usableSpace <= 0){
                    $("#estimatedUsableSpace").text("Not enough disks");
                    
                }else{
                    //Update the vol text
                    $("#totalDiskSumSize").text(bytesToSize(totalDiskSize));
                    $("#estimatedUsableSpace").text(bytesToSize(usableSpace));
                    $("#estimatedProtectionSpace").text(bytesToSize(redundancySpace));

                    //Update the bar size
                    $("#estimatedUsableSpace").css("width", ((usableSpace/totalDiskSize) * 100) + "%");
                    $("#estimatedProtectionSpace").css("width", ((redundancySpace/totalDiskSize) * 100) + "%");

                    let estimatedWastedSpace = (totalDiskSize - usableSpace - redundancySpace);
                    if (estimatedWastedSpace == 0){
                        $("#estimatedWastedSpace").hide();
                    }else{
                        $("#estimatedWastedSpace").show();
                        $("#estimatedWastedSpace").text(bytesToSize(estimatedWastedSpace));
                        $("#estimatedWastedSpace").css("width", ((estimatedWastedSpace/totalDiskSize) * 100) + "%");
                    }


                    if (usableSpace == totalDiskSize){
                        //RAID0
                        $("#estimatedProtectionSpace").hide();
                    }else{
                        $("#estimatedProtectionSpace").show();
                    }
                }

                //Check if disk is enough
                let enoughDisk = checkDiskNumber(diskSizes.length, selectedRAIDType);
                $("#matchRedundancy").html(enoughDisk?`<i class="ui green circle check icon"></i>`:`<i class="ui red circle times icon"></i>`);
                if (enoughDisk){
                    $("#capacityVisualizer").show();
                }else{
                    $("#capacityVisualizer").hide();
                }
            }


            //Check the number of disk is correct for given raidType
            function checkDiskNumber(selectedDisks, raidType) {
                // Define minimum required disk numbers for each RAID type
                const minDiskNumbers = {
                    "raid0": 2,
                    "raid1": 2,
                    "raid5": 3,
                    "raid6": 4,
                    "raid10": 4
                };

                // Check if RAID type is valid
                if (minDiskNumbers.hasOwnProperty(raidType)) {
                    const minDiskNumber = minDiskNumbers[raidType];
                    return selectedDisks >= minDiskNumber;
                } else {
                    return false;
                }
            }


            /* Create RAID functions */
            function createRAID(event){
                event.preventDefault();
                let volumeName = $("#volumeName").val().trim();
                if (volumeName == ""){
                    $('html, body').animate({ scrollTop: 0 }, 'slow');
                    $("#volumeName").parent().addClass("error");
                    return
                }

                //Test if the volume name is valid
                var pattern = /^[A-Za-z0-9\-_]+$/;
                if(!pattern.test(volumeName)){
                    $('html, body').animate({ scrollTop: 0 }, 'slow');
                    $("#volumeName").parent().addClass("error");
                    return
                }

                //Check if the raid selection is valid
                let selectedDiskCount = $(".installedDisk.active").length;
                let raidLevel = $("#raidtype")[0].value;
                if (!(checkDiskNumber(selectedDiskCount, raidLevel))){
                    $("#errorRaidType").text(raidLevel);
                    $("#notEnoughDiskErrorMessage").slideDown();
                    return
                }else{
                    $("#notEnoughDiskErrorMessage").slideUp();
                }

                //Generate a list of disk to erase
                erasePendingDisks = [];
                $(".installedDisk.active").each(function(){
                    let diskName = $(this).find(".diskname").text();
                    let diskID = $(this).attr("dname");
                    let diskSize = $(this).attr("dsize");
                    erasePendingDisks.push([diskID, diskName, parseInt(diskSize)]);
                });

                let earseDiskList = [];
                erasePendingDisks.forEach(diskToBeErased => {
                    earseDiskList.push(`/dev/${diskToBeErased[0]} (${diskToBeErased[1]} - ${bytesToSize(diskToBeErased[2])})`);
                });
                $("#erasePendingDisk").html(earseDiskList.join("<br>"));
               

                $("#volumeName").parent().removeClass("error");
                $("#confirmDiskChoice").modal("show");
            }

            //Confirm create an arary. 
            function confirmAddDisk(){
                createRAIDArray(true);
            }

            function confirmAddDiskWithoutFormat(){
                createRAIDArray(false);
            }

            function createRAIDArray(preformat=true){
                let volumeName = $("#volumeName").val().trim();
                let raidLevel = $("#raidtype")[0].value;

                //Generate the correct struct required by API endpoint to create raid
                let raidDev = [];
                erasePendingDisks.forEach(function(diskInfo){
                    raidDev.push(diskInfo[0]); //device path, e.g. /dev/sdb
                });

                //Reserved for future if needed
                let spareDev = [];

                $.ajax({
                    url: "../../../system/disk/raid/new",
                    method: "POST",
                    data: {
                        "raidName": volumeName,
                        "level": raidLevel,
                        "raidDev": JSON.stringify(raidDev),
                        "spareDev": JSON.stringify(spareDev),
                        "zerosuperblock": preformat
                    },
                    success: function(data){
                        if (data.error != undefined){
                            alert(data.error);
                        }else{
                            //Done
                            exitToParentWindow(data);
                        }

                    },
                    error: function(){
                        alert("Error occured when requesting create RAID endpoint");
                    }
                })
            }

            function exitToParentWindow(callbackdata){
                //Disk added. Handle callback to parent 
                if (ao_module_hasParentCallback()){
                    ao_module_parentCallback(callbackdata);
                }

                //Operation completed. 
                setTimeout(function(){
                    ao_module_close();
                }, 300);
            }
        </script>
    </body>
</html>